﻿
#define FMT_HEADER_ONLY
#define FMT_UNICODE 0
#include <fmt/core.h>
#include <fmt/ranges.h>

#include "plf_nanotimer.h"

#include <iostream>
#include <array>

#include "gul14/gul.h"

using namespace gul14;
using std::cout;

using namespace std::literals;

// clang-format off
typedef struct
{
    char a[48];
    char b[10]; // '0' - '9'
    char c[7];
    char d[6]; // 'a' - 'f'
    char e[26];
    char f[6]; // 'A' - 'F'
    char g[153];
} asc_table_st;

static const asc_table_st asc_table_ = {
    {}, 
    {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,}, 
    {}, 
    {10, 11, 12, 13, 14, 15,}, 
    {},
    {10, 11, 12, 13, 14, 15,}, 
    {},
};

static const unsigned char* asc_const_table = (const unsigned char*)&asc_table_;
// clang-format on

std::vector<uint8_t> to_bytes(const gul14::string_view& hexStr) noexcept
{
    std::vector<uint8_t> varr;
    if (hexStr.size() % 2)
    {
        //throw std::invalid_argument("Hex string length must be even");
        return varr;
    }

    uint8_t* src = (uint8_t*)hexStr.data();

    for (size_t i = 0; i < hexStr.size(); i += 2)
    {
        varr.push_back((asc_const_table[src[i]] << 4) | asc_const_table[src[i + 1]]);
    }

    return varr;
}

int main()
{
    const int cnt = 100000;
    const std::string hexstr = "11223344556677881122334455667788112233445566778811223344556677881122334455667788112233445566"
                          "77881122334455667788";

    {
        auto varr = to_bytes("zz");
        fmt::println("vector<uint8_t> {}", hex_string(varr.begin(), varr.end(), " "));
    }
    {
        auto varr = to_bytes("abcdefABCDEF0123456789");
        fmt::println("vector<uint8_t> {}", hex_string(varr.begin(), varr.end(), " "));
    }

    plf::nanotimer timer;
    timer.start();
    for (size_t i = 0; i < cnt; i++)
    {
        to_bytes(hexstr);
    }
    fmt::println("to_bytes {}", timer.get_elapsed_ms());

    auto const x = gul14::split("Test\nme\nnoe", "\n");
    for (auto s : x)
    {
        if (gul14::starts_with_nocase(s, "me"))
        {
            fmt::println("-- [{}]", s);
        }
        else
            fmt::println("-- {}", s);
    }

    std::string a   = "Hello World";
    auto        tok = gul14::split_sv(a, " ");
    for (auto s : tok)
    {
        fmt::println("-> {}", s);
    }

    unsigned char arr[5]{1, 2, 3, 4, 5};
    gul14::span<unsigned char> s(arr);
    fmt::println("span {}", hex_string(s.begin(), s.end()));

    std::array<unsigned char, 4> uc{
        {0, 1, 2, 255}
    };
    fmt::println("array {}", hex_string(uc.begin(), uc.end()));


    //! [Using the ThreadPool class]
    // Create a pool with 2 threads (returns a shared_ptr<ThreadPool>)
    auto pool = make_thread_pool(2);

    pool->add_task([]() {
        cout << "Task 1\n";
    });
    pool->add_task([]() {
        sleep(1);
        std::cout << "Task 2\n";
    });

    // Tasks can be scheduled to start later:
    // This one should start 2 seconds after enqueueing (if a thread is available)
    pool->add_task(
        []() {
            cout << "Task 3\n";
        },
        2s);

    // Probable output:
    // Task 1
    // Task 2
    // Task 3

    // Tasks can return results
    auto task = pool->add_task([]() {
        return 42;
    });

    while (not task.is_complete())
        sleep(0.1);

    // get_result() blocks until the task is complete
    cout << "Task result: " << task.get_result() << "\n";

    // Tasks can also interact with the pool themselves, e.g. to schedule a continuation:
    pool->add_task([](ThreadPool& pool) {
        cout << "Task 4\n";
        pool.add_task(
            []() {
                cout << "Task 5, a second later\n";
            },
            1s);
    });
    //! [Using the ThreadPool class]

    return 0;
}
